#include "sysdefs.h"

int contProcs;
int continuar;
int permissaoAddProc;
int idFilaSubmitSchedule, idFilaScheduleSubmit, idFilaScheduleList, idFilaListSchedule ,idFilaScheduleShutdown, maxProc;
procTable tabela;	

/*funcao ativada por sinal, na qual se remove as todas Filas criadas*/
void removerFilas(){
	struct msqid_ds dummy;
	msgctl(idFilaSubmitSchedule,IPC_RMID,&dummy);
	msgctl(idFilaScheduleSubmit,IPC_RMID,&dummy);
	msgctl(idFilaListSchedule,IPC_RMID,&dummy);
	msgctl(idFilaScheduleList,IPC_RMID,&dummy);
	msgctl(idFilaScheduleShutdown,IPC_RMID,&dummy);
	continuar = 0;
	printf("FILAS REMOVIDAS\n");
}

/*funcao ativada por sinal, na qual seta a flag permicaoAddProc, proibindo a entrada de novos processos*/
void proibirAddProc(){
	permissaoAddProc = 0;
	signal(SIGUSR1,removerFilas);/*troca o sinal que o chamou para a funcao removerFilas*/
}

unsigned int getTickCount(){
	struct timeval tv;
	if (gettimeofday(&tv, NULL) != 0)
		return 0;

	return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}

/*funcao ativada por sinal, na qual se mata todos os processos ainda ativos*/
void matarProcs(){
	int i;

	for(i=0;i<tabela.procInfoArray[0].numProc;i++){
		kill(tabela.procInfoArray[0].pidArray[i],SIGKILL);
	}
	contProcs = 0;
	tabela.numProcesses = 0;
}

/*funcao principal*/
int main(int argc,char *argv[]){
	intMsg msg;
	procInfo processo;
	int nReq = 0;
	int i;
	int j;
	unsigned int initialTime;
	int waitStatus;
	long curTime;
	int tmpPid;
	char* tempArgumentPasser[10];

	/* Pega o tempo de início do schedule */
	initialTime = getTickCount() / 1000;

	/*Inicializa mensagem com o pid de Schedule*/
	msg.dummyLong = getpid();
	msg.intMsg = getpid();
	
	/*testa se o argumento Passado na chamada do Schedule e valida.*/
	if(argc==2){
		maxProc = atoi(argv[1]);
		if(maxProc<0||maxProc>20){
			printf("ERRO: maximo de processos deve ser positivo e menor ou igual a 20\n");
			exit(1);
		}
	}else{
		printf("NUMERO DE PARAMETROS INVALIDO\n");
		exit(1);
	}
	/*Inicializar tabela de processos*/
	tabela.dummyLong = getpid();
	tabela.numProcesses = 0;
	contProcs = 0;

	continuar = 1;/*Flag para definir se o loop continua*/
	permissaoAddProc = 1;/*seta Permissao para adicionar novos processos como verdadeira*/
	signal(SIGUSR1,proibirAddProc);/*liga o SIGUSR1 com a funcao de proibir adicao de processos*/
	signal(SIGUSR2,matarProcs);/*liga o SIGUSR2 com a funcao de matar processos*/
	
	/*Criacao das Filas de Mensagens*/
	if((idFilaSubmitSchedule = msgget(CHAVE_FILA_SUBMIT_SCHEDULE,IPC_CREAT|0x1ff))<0){
		printf("ERRO SCHEDULE: ERRO NA CRIACAO DA FILA DO SUBMIT\n");
		exit(1);
	}
	if((idFilaScheduleSubmit = msgget(CHAVE_FILA_SCHEDULE_SUBMIT,IPC_CREAT|0x1ff))<0){
		printf("ERRO SCHEDULE: ERRO NA CRIACAO DA FILA DO SUBMIT\n");
		exit(1);
	}
	if((idFilaListSchedule = msgget(CHAVE_FILA_LIST_SCHEDULE,IPC_CREAT|0x1ff))<0){
		printf("ERRO SCHEDULE: ERRO NA CRIACAO DA FILA DO LIST\n");
		exit(1);
	}
	if((idFilaScheduleList = msgget(CHAVE_FILA_SCHEDULE_LIST,IPC_CREAT|0x1ff))<0){
		printf("ERRO SCHEDULE: ERRO NA CRIACAO DA FILA DO LIST\n");
		exit(1);
	}
	if((idFilaScheduleShutdown = msgget(CHAVE_FILA_SCHEDULE_SHUTDOWN,IPC_CREAT|0x1ff))<0){
		printf("ERRO SCHEDULE: ERRO NA CRIACAO DA FILA DO SHUTDOWN\n");
		exit(1);
	}

	/*Envia Pid para o shutdown, para que este mande sinais*/
	if(msgsnd(idFilaScheduleShutdown,&msg,sizeof(msg)-sizeof(long),0)==-1){
		printf("ERRO SCHEDULE: NAO FOI POSSIVEL EVNVIAR MSG AO SHUTDOWN\n");
		exit(1);
	}

	/*Inicio do main loop*/
	while(continuar){
		/*Tenta receber msg do List*/
		if(msgrcv(idFilaListSchedule,&msg,sizeof(intMsg)-sizeof(long),0,IPC_NOWAIT)>0){
			/*Envia Tabela para o List*/
			if(msgsnd(idFilaScheduleList,&tabela,sizeof(tabela)-sizeof(long),0)==-1){
				printf("ERRO SCHEDULE: NAO FOI POSSIVEL ENVIAR A TABELA DE PROCESSOS\n");
			}
		}
		/*Tenta receber msg do Submit*/
		if(msgrcv(idFilaSubmitSchedule,&processo,sizeof(procInfo)-sizeof(long),0,IPC_NOWAIT)>0){
			/*adiciona processo na Tabela com seus devidos parametros associados*/
			if(permissaoAddProc && tabela.numProcesses<maxProc){
				processo.state = PENDING;
				tabela.procInfoArray[tabela.numProcesses] = processo;
				tabela.procInfoArray[tabela.numProcesses].nReq = nReq;
				nReq++;
				tabela.numProcesses++;
				msg.intMsg = OK;
			}else{
				msg.intMsg = NOT_OK;
			}
			msgsnd(idFilaScheduleSubmit,&msg,sizeof(intMsg)-sizeof(long), 0);
		}
		/*testa se ha processos na fila*/
		if(tabela.numProcesses>0){
			/*Bota primeiro processo da Fila para rodar*/
			if(tabela.procInfoArray[0].state==PENDING){
				tabela.procInfoArray[0].startTime = (getTickCount() / 1000) - initialTime;
				for(i=0;i<tabela.procInfoArray[0].numProc;i++){
					tmpPid = fork();
					if(tmpPid<0){
						printf("ERRO SCHEDULE: ERRO NO FORK\n");
					}else if(tmpPid==0){
						/*Yuck! Passando os argumentos do programa para um array temporario
						 *para satisfazer o execv */
						for (j = 0; j < tabela.procInfoArray[0].argCount; j++) {
							tempArgumentPasser[j] = tabela.procInfoArray[0].programArgs[j];
						}
						tempArgumentPasser[j] = (char*) 0;

						if(execv(tabela.procInfoArray[0].programName, tempArgumentPasser)){
							printf("ERRO SCHEDULE: ERRO NO EXECV\n");
							exit(1);
						}
					}else{
						tabela.procInfoArray[0].pidArray[i] = tmpPid;
						contProcs++;
					}
				}
				if(contProcs!=0){
					tabela.procInfoArray[0].state = RUNNING;
				}
			}
			/*Testa tempo de execucao com tempo maximo, matando o processo se tiver maior que o tempo maximo*/
			else{
				curTime = (getTickCount() / 1000) - initialTime;

				/* Checa se os filhos morreram */
				for(i = 0; i < tabela.procInfoArray[0].numProc; i++) {
					if (waitpid(tabela.procInfoArray[0].pidArray[i], &waitStatus, WNOHANG) == tabela.procInfoArray[0].pidArray[i]) {
						contProcs--;
					}
				}

				if(contProcs==0){
					for(i=0;i<tabela.numProcesses-1;i++){
						tabela.procInfoArray[i] = tabela.procInfoArray[i+1];
					}
					tabela.numProcesses--;
				}else if(tabela.procInfoArray[0].startTime + tabela.procInfoArray[0].execSeconds < curTime){
					for(i=0;i<tabela.procInfoArray[0].numProc;i++){
						kill(tabela.procInfoArray[0].pidArray[i], SIGKILL); 
					}
				}
			}
		}else if(!permissaoAddProc){
			msgsnd(idFilaScheduleShutdown,&msg,sizeof(intMsg)-sizeof(long),0);
			pause();
		}
	}
	exit(0);
}
